package edu.hawaii.nearestneighbor.tests;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import edu.hawaii.nearestneighbor.classifiers.CamClassifier;
import edu.hawaii.nearestneighbor.util.Gamma;
import edu.hawaii.nearestneighbor.util.NNutil;
import edu.hawaii.nearestneighbor.util.Parameters;
import edu.hawaii.nearestneighbor.util.DataPoint;

/**
 * Tests cases for the Cam classifier.
 * @author Robert Puckett
 *
 */
public class TestCamClassifier {

  /**
   * 
   */
  @Test
  public void testClassifyListOfDataPointDataPoint() {
    new CamClassifier();
    fail("Not yet implemented");
  }

  /**
   * 
   */
  @Test
  public void testClassifyListOfDataPointDataPointInt() {
    fail("Not yet implemented");
  }

  /**
   * 
   */
  @Test
  public void testTrainParameters() {
    fail("Not yet implemented");
  }

  /**
   * 
   */
  @Test
  public void testCamDist() {
    
    Parameters p1 = new Parameters(1.0,1.0, new ArrayList<Double>());
    List<Parameters> A = new ArrayList<Parameters>();
    A.add(p1);
    List<DataPoint> data = new ArrayList<DataPoint>();
    DataPoint dp1 = new DataPoint();
    data.add(dp1);
    DataPoint q = new DataPoint();
    
    CamClassifier.camDist(A, 0, data, q);
    
  }


  /**
   * Tests the calculation of constants 1 and 2.
   */
  @Test
  public void testCalcConstants() {
    int p = 15;
    double p1 = Gamma.gamma(((double)p + 1.0) / 2.0);
    double p2 = Gamma.gamma((double)p / 2.0);
    double sqrt2 = Math.sqrt(2.0);
    double c2 = sqrt2 * p1 / p2;
    double c1 = c2 / (double)p;
    assertEquals("Testing calculation of C1", c1, CamClassifier.calcConstant1(p));
    assertEquals("Testing calculation of C2", c2, CamClassifier.calcConstant2(p));
  }

  /**
   * Tests the calculation of constant 1.
   */
  @Test
  public void testCalcConstant1Int() {
    fail("Not yet implemented");
  }

  /**
   * Tests the calculation of constant 1, alternate function.
   */
  @Test
  public void testCalcConstant1DoubleInt() {
    fail("Not yet implemented");
  }

  /**
   * Tests the calculation of constant W matrix.
   */
  @Test
  public void testCalcW() {
    List<DataPoint> near = new ArrayList<DataPoint>();

    List<Double> x1 = new ArrayList<Double>();
    x1.add(4.0);
    x1.add(5.0);
    List<Double> x2 = new ArrayList<Double>();
    x2.add(1.0);
    x2.add(2.0);
    
    DataPoint p1 = new DataPoint(x1,1);
    DataPoint p2 = new DataPoint(x2,1);
    DataPoint p3 = new DataPoint(x1,0);
    DataPoint p4 = new DataPoint(x2,0);
    
    near.add(p1);
    near.add(p2);
    near.add(p3);
    near.add(p4);
    
    List<List<Double>> W = CamClassifier.calcW(near, p1);
    assertEquals("Testing value of W.0.0",0.0,W.get(0).get(0));
    assertEquals("Testing value of W.0.1",0.0,W.get(0).get(1));
    assertEquals("Testing value of W.1.0",1.0 - 4.0,W.get(1).get(0));
    assertEquals("Testing value of W.1.1",2.0 - 5.0,W.get(1).get(1));
    assertEquals("Testing value of W.2.0",-0.0,W.get(2).get(0));
    assertEquals("Testing value of W.2.1",-0.0,W.get(2).get(1));
    assertEquals("Testing value of W.3.0",-0.5 * (1.0 - 4.0),W.get(3).get(0));
    assertEquals("Testing value of W.3.1",-0.5 * (2.0 - 5.0),W.get(3).get(1));
    
  }

  /**
   * Tests the calculation of hat(G).
   */
  @Test
  public void testCalcGhat() {
    List<DataPoint> near = new ArrayList<DataPoint>();

    List<Double> x1 = new ArrayList<Double>();
    x1.add(4.0);
    x1.add(5.0);
    List<Double> x2 = new ArrayList<Double>();
    x2.add(1.0);
    x2.add(2.0);
    
    DataPoint p1 = new DataPoint(x1,1);
    DataPoint p2 = new DataPoint(x2,1);
    DataPoint p3 = new DataPoint(x1,0);
    DataPoint p4 = new DataPoint(x2,0);
    
    near.add(p1);
    near.add(p2);
    near.add(p3);
    near.add(p4);
    
    List<List<Double>> W = CamClassifier.calcW(near, p1);
    
    int k = 4;
    
    List<Double> gHat = CamClassifier.calcGhat(W, k);
    
    assertEquals("Test calc of hat(G) at 0", ((1.0 - 4.0) - 0.5 * (1.0 - 4.0)) / 4.0,gHat.get(0));
    assertEquals("Test calc of hat(G) at 1", ((2.0 - 5.0) - 0.5 * (2.0 - 5.0)) / 4.0,gHat.get(1));
  }

  /**
   * Tests the calculation of hat(L).
   */
  @Test
  public void testCalcLhat() {
    List<DataPoint> near = new ArrayList<DataPoint>();

    List<Double> x1 = new ArrayList<Double>();
    x1.add(4.0);
    x1.add(5.0);
    List<Double> x2 = new ArrayList<Double>();
    x2.add(1.0);
    x2.add(2.0);
    
    DataPoint p1 = new DataPoint(x1,1);
    DataPoint p2 = new DataPoint(x2,1);
    DataPoint p3 = new DataPoint(x1,0);
    DataPoint p4 = new DataPoint(x2,0);
    
    near.add(p1);
    near.add(p2);
    near.add(p3);
    near.add(p4);
    
    List<List<Double>> W = CamClassifier.calcW(near, p1);
    
    int k = 4;

    double result = NNutil.magnitude(W.get(0));
    result += NNutil.magnitude(W.get(1));
    result += NNutil.magnitude(W.get(2));
    result += NNutil.magnitude(W.get(3));
    result = result / (double)k;
    
    assertEquals("Test calc of hat(L)", result,CamClassifier.calcLhat(W, k));
    
  }

  /**
   * Tests the calculation of Tau vector.
   */
  @Test
  public void testCalcTau() {
    List<DataPoint> near = new ArrayList<DataPoint>();

    List<Double> x1 = new ArrayList<Double>();
    x1.add(4.0);
    x1.add(5.0);
    List<Double> x2 = new ArrayList<Double>();
    x2.add(1.0);
    x2.add(2.0);
    
    DataPoint p1 = new DataPoint(x1,1);
    DataPoint p2 = new DataPoint(x2,1);
    DataPoint p3 = new DataPoint(x1,0);
    DataPoint p4 = new DataPoint(x2,0);
    
    near.add(p1);
    near.add(p2);
    near.add(p3);
    near.add(p4);
    
    List<List<Double>> W = CamClassifier.calcW(near, p1);
    
    int k = 4;
    
    List<Double> gHat = CamClassifier.calcGhat(W, k);

    
    List<Double> tau = CamClassifier.calcTau(gHat);
    
    double gmag = NNutil.magnitude(gHat); 
    
    assertEquals("Test calculation of Tau matrix, 0", gHat.get(0) / gmag, tau.get(0) );
    assertEquals("Test calculation of Tau matrix, 0", gHat.get(1) / gmag, tau.get(1) );
  }



}
